#include "Middlewares/AUDIO_CODEC/mp3/mp3_dec.h"
#include "Middlewares/AUDIO_CODEC/mp3/helix/mp3dec.h"
#include "string.h"

mp3_decoder_info_t *mp3_decoder_info = NULL; /* mp3控制结构体 */
HMP3Decoder mp3_decoder = NULL;              /* mp3解码器句柄 */

/**
 * @brief       MP3解码器初始化
 * @param       void
 * @retval      0, 正常
 *              其他, 失败
 */
uint8_t mp3_decoder_init(void)
{
    if (mp3_decoder_info) /* 已经初始化过 */
        mp3_decoder_deinit();

    mp3_decoder_info = MP3_MALLOC(sizeof(mp3_decoder_info_t));
    mp3_decoder = MP3InitDecoder();

    if (!mp3_decoder_info || !mp3_decoder)
    {
        mp3_decoder_deinit();
        MP3_LOG("mp3 decoder malloc failed!\r\n");
        return 1;
    }

    memset(mp3_decoder_info, 0, sizeof(mp3_decoder_info_t));
    return 0;
}

/**
 * @brief       MP3解码器反初始化
 * @param       void
 * @retval      0, 正常
 *              其他, 失败
 */
uint8_t mp3_decoder_deinit(void)
{
    if (mp3_decoder_info)
        MP3_FREE(mp3_decoder_info);

    if (mp3_decoder)
        MP3FreeDecoder(mp3_decoder);

    mp3_decoder = NULL;
    mp3_decoder_info = NULL;
    return 0;
}

/**
 * @brief       解析ID3V1
 * @param       buf             : 输入数据缓存区(大小固定是128字节)
 * @param       pctrl           : MP3控制器
 * @retval      0, 正常
 *              其他, 失败
 */
uint8_t mp3_id3v1_decode(uint8_t *buf, mp3_decoder_info_t *info)
{
    ID3V1_Tag *id3v1tag;
    id3v1tag = (ID3V1_Tag *)buf;

    if (strncmp("TAG", (char *)id3v1tag->id, 3) == 0) /* 是MP3 ID3V1 TAG */
    {
        if (id3v1tag->title[0])
            strncpy((char *)info->title, (char *)id3v1tag->title, 30);

        if (id3v1tag->artist[0])
            strncpy((char *)info->artist, (char *)id3v1tag->artist, 30);
    }
    else
        return 1;

    return 0;
}

/**
 * @brief       解析ID3V2
 * @param       buf             : 输入数据缓存区(大小固定是128字节)
 * @param       size            : 数据大小
 * @param       info           : MP3控制器
 * @retval      0, 正常
 *              其他, 失败
 */
uint8_t mp3_id3v2_decode(uint8_t *buf, uint32_t size, mp3_decoder_info_t *info)
{
    ID3V2_TagHead *taghead;
    ID3V23_FrameHead *framehead;
    uint32_t t;
    uint32_t tagsize;    /* tag大小 */
    uint32_t frame_size; /* 帧大小 */
    taghead = (ID3V2_TagHead *)buf;

    if (strncmp("ID3", (const char *)taghead->id, 3) == 0) /* 存在ID3? */
    {
        tagsize = ((uint32_t)taghead->size[0] << 21) | ((uint32_t)taghead->size[1] << 14) | ((uint16_t)taghead->size[2] << 7) | taghead->size[3]; /* 得到tag 大小 */
        info->offset = tagsize;                                                                                                                   /* 得到mp3数据开始的偏移量 */

        if (tagsize > size)
            tagsize = size; /* tagsize大于输入bufsize的时候,只处理输入size大小的数据 */

        if (taghead->mversion < 3)
        {
            MP3_LOG("not supported mversion!\r\n");
            return 1;
        }

        t = 10;

        while (t < tagsize)
        {
            framehead = (ID3V23_FrameHead *)(buf + t);
            frame_size = ((uint32_t)framehead->size[0] << 24) | ((uint32_t)framehead->size[1] << 16) | ((uint32_t)framehead->size[2] << 8) | framehead->size[3]; /* 得到帧大小 */

            if (strncmp("TT2", (char *)framehead->id, 3) == 0 || strncmp("TIT2", (char *)framehead->id, 4) == 0) /* 找到歌曲标题帧,不支持unicode格式!! */
            {
                strncpy((char *)info->title, (char *)(buf + t + sizeof(ID3V23_FrameHead) + 1), ((frame_size - 1) < (40 - 1) ? (frame_size - 1) : (40 - 1)));
            }

            if (strncmp("TP1", (char *)framehead->id, 3) == 0 || strncmp("TPE1", (char *)framehead->id, 4) == 0) /* 找到歌曲艺术家帧 */
            {
                strncpy((char *)info->artist, (char *)(buf + t + sizeof(ID3V23_FrameHead) + 1), ((frame_size - 1) < (40 - 1) ? (frame_size - 1) : (40 - 1)));
            }

            t += frame_size + sizeof(ID3V23_FrameHead);
        }
    }
    else
        info->offset = 0; /* 不存在ID3,mp3数据是从0开始 */

    return 0;
}

/**
 * @brief       获取MP3参数
 * @param       buf            : MP3数据缓存区
 * @param       buf_len        : 输入数据缓存区大小
 * @param       objsize        : 文件大小,用于CBR计算总时长
 * @param       info           : MP3控制器
 * @retval      0: 成功 1: 参数错误 2: 未找到同步字 3: 获取帧信息失败
 */
uint8_t mp3_get_params(uint8_t *buf, uint32_t buf_len, uint32_t objsize, mp3_decoder_info_t *info)
{
    MP3FrameInfo frame_info;
    MP3_FrameXing *fxing;
    MP3_FrameVBRI *fvbri;
    int16_t samples_per_frame; /* 一帧的采样个数 */
    uint32_t totframes;        /* 总帧数 */
    uint32_t p_offset;
    int offset = 0; /* 偏移量 */

    if (!buf || !info || !mp3_decoder) /* 参数错误 */
        return 1;

    offset = MP3FindSyncWord(buf, buf_len); /* 查找同步字 */
    if (offset < 0)                         /* 没有找到同步字 */
    {
        MP3_LOG("no sync word\r\n");
        return 2;
    }

    if (MP3GetNextFrameInfo(mp3_decoder, &frame_info, buf + offset) != 0) /* 获取帧信息 */
    {
        MP3_LOG("get frame info failed!\r\n");
        return 3;
    }

    p_offset = offset + 4 + 32;                /* 指向帧头后面的位置 */
    fvbri = (MP3_FrameVBRI *)(buf + p_offset); /* 指向VBRI帧 */

    if (strncmp("VBRI", (char *)fvbri->id, 4) == 0) /* VBRI帧 */
    {
        if (frame_info.version == MPEG1)
            samples_per_frame = 1152; /* MPEG1,layer3每帧采样数等于1152 */
        else
            samples_per_frame = 576; /* MPEG2/MPEG2.5,layer3每帧采样数等于576 */

        totframes = ((uint32_t)fvbri->frames[0] << 24) | ((uint32_t)fvbri->frames[1] << 16) | ((uint16_t)fvbri->frames[2] << 8) | fvbri->frames[3]; /* 得到总帧数 */
        info->totsec = totframes * samples_per_frame / frame_info.samprate;
    }
    else /* 不是VBRI帧,则可能是Xing帧 */
    {
        if (frame_info.version == MPEG1) /* MPEG1 */
        {
            p_offset = frame_info.nChans == 2 ? 32 : 17; /* 指向Xing帧,双声道偏移32,单声道偏移17 */
            samples_per_frame = 1152;                    /* MPEG1,layer3每帧采样数等于1152 */
        }
        else
        {
            p_offset = frame_info.nChans == 2 ? 17 : 9; /* 指向Xing帧,双声道偏移17,单声道偏移9 */
            samples_per_frame = 576;                    /* MPEG2/MPEG2.5,layer3每帧采样数等于576 */
        }
        p_offset += offset + 4;                    /* 指向帧头后面的位置 */
        fxing = (MP3_FrameXing *)(buf + p_offset); /* 指向Xing帧 */

        if (strncmp("Xing", (char *)fxing->id, 4) == 0) /* Xing帧 */
        {
            totframes = ((uint32_t)fxing->frames[0] << 24) | ((uint32_t)fxing->frames[1] << 16) | ((uint16_t)fxing->frames[2] << 8) | fxing->frames[3]; /* 得到总帧数 */
            info->totsec = totframes * samples_per_frame / frame_info.samprate;
        }
        else if (strncmp("Info", (char *)fxing->id, 4) == 0) /* Info帧 */
        {
            totframes = ((uint32_t)fxing->frames[0] << 24) | ((uint32_t)fxing->frames[1] << 16) | ((uint16_t)fxing->frames[2] << 8) | fxing->frames[3]; /* 得到总帧数 */
            info->totsec = totframes * samples_per_frame / frame_info.samprate;
        }
        else /* 既不是VBRI帧,也不是Xing/Info帧,则是CBR */
        {
            info->totsec = objsize * 8 / frame_info.bitrate; /* CBR计算总时间 */
        }
    }
    info->bitrate = frame_info.bitrate;        /* 获取比特率 */
    info->samprate = frame_info.samprate;      /* 获取采样率 */
    info->outsamples = frame_info.outputSamps; /* 获取输出采样数 */
    info->nChans = frame_info.nChans;          /* 获取声道数 */

    return 0;
}

/**
 * @brief       MP3解码一帧数据
 * @param       buf            : 输入数据缓存区
 * @param       buf_len        : 输入数据缓存区大小(默认418字节)
 * @param       pcm            : 输出PCM数据缓存区(默认大小2304*2字节)
 * @param       bytesLeft      : 输入数据缓存区剩余大小
 * @param       info           : MP3控制器
 * @retval      0: 成功 1: 参数错误 2: 未找到同步字 3: 解码失败
 */
uint8_t mp3_decoder_data(uint8_t *buf, uint32_t buf_len, int16_t *pcm, int16_t *bytesLeft, mp3_decoder_info_t *info)
{
    int offset = 0; /* 偏移量 */
    uint8_t *inbuf;
    int err;
    int res = 0;
    MP3FrameInfo mp3frameinfo;

    if (!buf || !pcm || !info)
        return 1;

    offset = MP3FindSyncWord(buf, buf_len); /* 在readptr位置,开始查找同步字符 */

    if (offset < 0) /* 没有找到同步字符,跳出帧解码循环 */
    {
        MP3_LOG("no sync word\r\n"); /* 没找到帧同步字符 */
        res = 2;
    }

    inbuf = buf + offset;          /* 指向同步字位置 */
    *bytesLeft = buf_len - offset; /* 输入数据缓存区剩余大小 */

    err = MP3Decode(mp3_decoder, &inbuf, (int *)bytesLeft, (short *)pcm, 0); /* 解码一帧MP3数据 */

    if (err != ERR_MP3_NONE) /* 解码失败 */
    {
        MP3_LOG("decode error:%d\r\n", err);
        res = 3;
    }

    MP3GetLastFrameInfo(mp3_decoder, &mp3frameinfo); /* 得到刚刚解码的MP3帧信息 */

    info->bitrate = mp3frameinfo.bitrate;
    info->samprate = mp3frameinfo.samprate;
    info->outsamples = mp3frameinfo.outputSamps;
    info->nChans = mp3frameinfo.nChans;

    return res;
}
